﻿using UnityEngine;

namespace AI.Graph {


    public interface IListIndex {

        int listIndex { get; set; }

    }

    public class List<TKey, TValue> where TKey : IListIndex where TValue : IListIndex {

        private const int defaultMaxSize = 8;
        private int m_startDefaulSize;
        private TKey[] m_keys;
        private TValue[] m_values;
        private int arrayLength => m_keys.Length;

        public int length { get; private set; }

        public TKey this [int index] => GetKey (index);

        public TValue this [TKey key] => GetValue (key);


        public List () : this (defaultMaxSize) { }


        public List (int size) {
            m_startDefaulSize = size;
            m_keys = new TKey[m_startDefaulSize];
            m_values = new TValue[m_startDefaulSize];
            length = 0;
        }


        public void Add (TKey key, TValue value) {
            if (length >= arrayLength) {
                TKey[] newKeys = new TKey[length + defaultMaxSize];
                TValue[] newValues = new TValue[length + defaultMaxSize];

                for (int i = 0; i < arrayLength; i++) {
                    newKeys[i] = m_keys[i];
                    newValues[i] = m_values[i];
                }

                m_keys = newKeys;
                m_values = newValues;
            }

            key.listIndex = length;
            value.listIndex = length;

            m_keys[length] = key;
            m_values[length] = value;
            length++;
        }


        public bool Remove (TKey key) {
            if (!ContainsKey (key)) {
                return false;
            }

            Remove (key.listIndex);
            return true;
        }


        public bool Remove (TValue value) {
            if (!ContainsValue (value)) {
                return false;
            }

            Remove (value.listIndex);
            return true;
        }


        public void Remove (int index) {
            m_keys[index] = default;
            m_values[index] = default;

            for (int i = index; i < length - 1; i++) {
                m_keys[index] = m_keys[index + 1];
                m_keys[index].listIndex = index;

                m_values[index] = m_values[index + 1];
                m_values[index].listIndex = index;
            }

            length--;
        }


        public void CLear () {
            m_keys = new TKey[m_startDefaulSize];
            m_values = new TValue[m_startDefaulSize];
            length = 0;
        }


        public bool ContainsKey (TKey key) {
            if (key.listIndex < 0 || key.listIndex >= length) return false;
            return Equals (m_keys[key.listIndex], key);
        }


        public bool ContainsValue (TValue value) {
            return Equals (m_values[value.listIndex], value);
        }


        public TKey GetKey (int index) {
            return m_keys[index];
        }


        public TValue GetValue (int index) {
            return m_values[index];
        }


        public TKey GetKey (TValue value) {
            return ContainsValue (value) ? m_keys[value.listIndex] : default;
        }


        public TValue GetValue (TKey key) {
            return ContainsKey (key) ? m_values[key.listIndex] : default;
        }


        public void SetFirstItem (TKey key) {
            if (!ContainsKey (key)) {
                Debug.Log ("The given item is not in this list.");
                return;
            }

            for (int i = key.listIndex; i > 0; i--) {
                Swap (GetKey (i), GetKey (i - 1));
            }
        }


        void Swap (TKey key1, TKey key2) {
            TValue value1 = GetValue (key1);
            TValue value2 = GetValue (key2);
            m_keys[key1.listIndex] = key2;
            m_keys[key2.listIndex] = key1;
            m_values[key1.listIndex] = value2;
            m_values[key2.listIndex] = value1;
            int index = key1.listIndex;
            key1.listIndex = key2.listIndex;
            key2.listIndex = index;
            value1.listIndex = value2.listIndex;
            value2.listIndex = index;
        }

    }


}